﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once

#include "GcmpCommandAction.h"
#include "ActionBase.h"
#include "Vector3d.h"
#include "GcmpApiDeclaration.h"
#include "SnapType.h"

namespace gcmp
{
    class ISnap;
    class ISnapContext;
    class Vector3d;
    class IPlane;
    class IPickPointActionConfig;

    /// \brief IPickPointAction的通信结构体
    struct GCMP_COMMAND_ACTION_EXPORT PickPointExchangeData
    {
        /// \brief 拾取点的类型
        enum class PickPointType : Int32
        {
            TwoDimensionPoint = 0, /// 要求拾取的是二维点，即最终返回的结果只有x，y值有效，z值无效，但可以获取到值，值始终为0
            ThreeDimensionPoint /// 要求拾取的是三维点，即最终返回的结果x，y，z均有效，默认拾取的是三维点
        };

        /// \brief 捕捉平面的类型
        enum class SnapPlaneType : Int32
        {
            WorkPlane = 0,                      /// < 使用工作平面
            FirstPointPlane = 1,                /// < 根据当前Camera方向构造平面,第一个捕捉输入点为该平面原点
            SnapPlane = 2,                      /// < 用户自定义的捕捉平面
        };

        /// \brief 构造PickPointExchangeData
        ///
        /// \param pPickedPointInput    鼠标左键按下Pick到的点(射线与工作平面的交点, 受捕捉影响)
        /// \param pUserCancelledInput  action是否被用户取消
        /// \param pPickedPointsInput    之前已经picked的坐标集
        /// \param pSnapInput    捕捉结果
        /// \param pCommands    激活的命令集
        /// \param planeType 捕捉平面的类型，默认工作平面
        /// \param pickElementBeforeSnapConcretePoint 是否在捕捉具体点之前先做一次拾取对象的操作
        PickPointExchangeData(Vector3d* pPickedPointInput,
            bool* pUserCancelledInput,
            std::vector<gcmp::Vector3d>* pPickedPointsInput,
            OwnerPtr<ISnap>* pSnapInput = nullptr,
            const std::vector<std::wstring>* pCommands = nullptr,
            SnapPlaneType planeType = SnapPlaneType::WorkPlane,
            bool pickElementBeforeSnapConcretePoint = false,
            PickPointType pickPointType = PickPointType::ThreeDimensionPoint
        );

        Vector3d* pPickedPoint;  // 实际Pick到的点(射线与工作平面的交点, 受捕捉影响)
        Vector3d mousePoint; // 鼠标点 (不受捕捉影响)
        bool *pUserCancelled;
        OwnerPtr<ISnap>* pSnap;
        std::vector<std::wstring> activatableCommands;
        std::vector<gcmp::Vector3d>* pPickedPoints; // 之前已经picked的坐标点
        SnapPlaneType m_planeType;
        bool m_pickElementBeforeSnapConcretePoint;
        PickPointType m_pickPointType;
    };

    
    class GCMP_COMMAND_ACTION_EXPORT IPickPointAction : public gcmp::ActionBase
    {
    public:
        /// \brief 回调函数
        ///
        /// \param pCurrentView    当前Ui视图
        /// \param pickedPt  Pick到的点(射线与工作平面的交点)
        /// \param mousePt   鼠标点
        typedef std::function<void(gcmp::IUiView* pCurrentView, const gcmp::Vector3d& pickedPt, const gcmp::Vector3d& mousePt)> MoveCallback;

        /// \brief 创建回调函数
        /// \param *func    T对象的函数指针
        /// \param object  T对象
        template<typename T> 
        static MoveCallback CreateMoveCallback(
            void (T::*func)(gcmp::IUiView*, const gcmp::Vector3d& pos, const gcmp::Vector3d& mousePt),
            T* object) 
        {
            if (object)
            {
                return std::bind(func, object, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
            }
            return nullptr;
        }

    public:
        /// \brief 函数封装器，用于存储“设置自定义光标类型的回调函数”
        ///
        /// \param cursorPath 自定义光标类型的路径
        /// \return 设置成功返回true，失败返回false
        typedef std::function<bool(std::wstring & cursorPath)> CursorPathCallBack;

        /// \brief 设置自定义光标类型的回调函数
        ///
        /// \param callback 用户自定义的回调函数
        /// \return 设置成功返回true，失败返回false
        virtual bool SetCursorPathCallBack(CursorPathCallBack callback) = 0;

        /// \brief 函数封装器，用于存储“设置 移动鼠标点执行捕捉前 的回调函数”
        ///
        /// \param pCurrentView 当前UiView
        /// \param mousePt      实时鼠标位置
        /// \param pSnapContext 捕捉上下文
        /// \return void
        typedef std::function<void(IUiView* pCurrentView, const Vector3d& mousePt, ISnapContext* pSnapContext)> MoveBeforeSnapCallBack;

        template<typename T>
        static MoveBeforeSnapCallBack CreateMoveBeforeSnapCallBack(void (T::*pFunc)(IUiView* pCurrentView, const Vector3d& mousePt, ISnapContext* pSnapContext), T* pObject)
        {
            if (pObject)
            {
                return std::bind(pFunc, pObject, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
            }
            return nullptr;
        }

        /// \brief 设置 移动鼠标点执行捕捉前 的回调函数
        ///
        /// \param callback 用户自定义的回调函数
        /// \return 设置成功返回true，失败返回false
        virtual bool SetMoveBeforeSnapCallBack(MoveBeforeSnapCallBack callback) = 0;

    public:

        /// \brief 回调函数
        /// \\ 返回值为ture，退出OnKeyDown； 返回值为false，继续执行OnKeyDown的默认行为
        /// \param nChar   键盘按钮
        /// \param pCurrentView    当前Ui视图
        /// \param pos  Pick到的点(射线与工作平面的交点)
        /// \param mousePt   鼠标点
        typedef std::function<bool(int nChar, gcmp::IUiView*, const gcmp::Vector3d& pickedPt, const gcmp::Vector3d& mousePt)> OnKeyDownCallback;

        /// \brief 创建键盘按钮响应函数的回调函数
        /// \param *func    T对象的函数指针
        /// \param object  T对象
        template<typename T>
        static OnKeyDownCallback CreateOnKeyDownCallback(
            bool (T::*func)(int nChar, gcmp::IUiView*, const gcmp::Vector3d& pickedPt, const gcmp::Vector3d& mousePt),
            T* object)
        {
            if (object)
            {
                return std::bind(func, object, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
            }
            return nullptr;
        }

        /// \brief 设置OnKeyDown的回调函数
        ///
        /// \param callback 用户自定义的回调函数
        /// \return 设置成功返回true，失败返回false
        virtual bool SetOnKeyDownCallback(OnKeyDownCallback callback) = 0;

    public:
        /// \brief 编辑回调函数
        ///
        /// \param editValue 输入框输入的值
        /// \param coorDinate 输入值对应的坐标值
        /// \param errorTip 无法找到对应的坐标值时的出错提示
        /// \return 匹配成功返回true，失败返回false
        typedef std::function<bool(const std::wstring &editValue, double &coorDinate, std::wstring &errorTip)> EditCallback;

        /// \brief 注册编辑回调函数
        ///
        /// \param editCallback 编辑回调函数
        /// \return 成功返回true，失败返回false
        virtual bool RegisterEditCallBack(EditCallback &editCallback) = 0;

    public:
        /// \brief 创建IPickPointAction对象
        ///
        /// \param exchangeData    IPickPointAction的通信结构体
        /// \param moveCallback  回调函数
        /// \param enActionFinishedMode   action结束方式
        /// \param isReturnKeyNeedCanceled   设置用户按了Enter和Retrun键是否退出，缺省不退出
        /// \param opPickConfig   用户可以通过这个接口配置一些用户定制的行为（如：配置拾取时的正交模式）
        static gcmp::OwnerPtr<IPickPointAction> Create(PickPointExchangeData &exchangeData, MoveCallback moveCallback = nullptr, 
            ActionFinishedMode enActionFinishedMode = ActionFinishedMode::LButtonDown,bool isReturnKeyNeedCanceled = false, OwnerPtr<IPickPointActionConfig> opPickConfig = nullptr);
        
    public:
        /// \brief 是否捕捉
        ///
        /// \return  true表示是，false表示不是
        virtual bool IsCaught() const = 0;

        /// \brief 获取捕捉输入信息对象
        ///  注意：目前通过ISnapContext接口的SetSnapSettings配置不生效，请使用IPickPointActionConfig接口配置
        virtual ISnapContext* GetSnapContext() const = 0;

        /// \brief 设置捕捉输入信息对象
        ///  注意：目前通过ISnapContext接口的SetSnapSettings配置不生效，请使用IPickPointActionConfig接口配置
        /// \return  true表示成功，false表示失败
        virtual bool SetSnapContext(const ISnapContext* snapContext) = 0;

        /// \brief 捕捉具体点之前先做一次拾取操作的拾取容差设置
        /// \param pixelCount 像素值大小
        /// \return true表示成功，false表示失败
        virtual bool SetPickTolarenceBeforeSnapConcretePoint(int pixelCount) = 0;

        /// \brief 设置第一个捕捉输入点。在捕捉第二点时有效
        /// \param firstPoint 第一个输入点
        /// \return 成功返回true，失败返回false
        virtual bool SetFirstPoint(const Vector3d& firstPoint) = 0;

        /// \brief 开启坐标输入对话框
        /// \param userCustomInput 是否支持自定义输入,自定义字符输入结束pick，不验证错误重新输入
        /// \param opWorkPlane 用户指定工作平面，默认为空显示显示全局坐标，否则显示用户相对坐标
        /// \return 成功返回true，失败返回false
        virtual bool StartCoordinateInput(OwnerPtr<IPlane> opWorkPlane = nullptr, bool userCustomInput = false) = 0;

        /// \brief 为OnKeyDown的默认Tab键切换捕捉对象的行为设置过滤捕捉类型
        /// \param needFilterSnapType 需要过滤的捕捉类型
        /// \return 成功返回true，失败返回false
        virtual bool SetFilterSnapTypeForTabKeySwitch(std::vector<SnapType> needFilterSnapType) = 0;
    };
}

